/*
 * @Author: qinuoyun
 * @Date:   2019-09-28 13:29:03
 * @Last Modified by:   qinuoyun
 * @Last Modified time: 2021-10-15 10:33:26
 */
const { MongoClient, ObjectId } = require('mongodb');
/**
 * 数据库处理-75300
 */
class mongodb {
    /**
     * 初始化构造
     * @return {[type]} [description]
     */
    constructor(config) {
        this.exp = {
            "=": "",
            "eq": "",
            ">": "gt",
            "gt": "gt",
            "<": "lt",
            "lt": "lt",
            ">=": "gte",
            "gte": "gte",
            "<=": "lte",
            "lte": "lte",
            "!=": "ne",
            "ne": "ne",
        }
        //配置文件
        this.config = config;
        //设置数据库
        this.database = config.database;
        //处理选项
        this.options = {
            name: '',
            where: {},
            order: { '_id ASC': -1 },
            fields: ''
        };
        //处理URI链接
        if (config) {
            let hostname = "";
            let replicaSet = "";
            if (is_array(config.hostname)) {
                hostname = implode(",", config.hostname);
            } else {
                hostname = config.hostname + ":" + config.hostport;
            }
            if (config.replicaSet) {
                replicaSet = "?replicaSet=" + config.replicaSet
            }
            //设置是否需要账号密码链接
            if (config.username) {
                this.config.uri = `mongodb://${config.username}:${config.password}@${hostname}/${config.database}${replicaSet}`;
            } else {
                this.config.uri = `mongodb://${hostname}/${config.database}${replicaSet}`;

            }
        }
    }

    /**
     * 数据库链接
     * @return {[type]} [description]
     */
    async connection() {
        try {
            let connect = await MongoClient.connect(this.config.uri, { useNewUrlParser: true, useUnifiedTopology: true });
            return connect;
        } catch (error) {
            throw new Error('数据库链接失败');
        }
    }

    /**
     * 表名
     * @return {[type]} [description]
     */
    name(name) {
        //初始化请空配置
        this.options = {
            name: name,
            where: {},
            order: { '_id ASC': -1 },
            fields: ''
        }
        return this;
    }

    /**
     * 设置字段
     * @param  {String} fields [description]
     * @return {[type]}        [description]
     */
    fields(fields = "") {
        if (empty(this.options["fields"])) {
            let _fields = "";
            if (is_array(fields)) {
                _fields = fields.join(' ');
            } else {
                _fields = fields;
            }
            // //设置排除字段
            // if (this.db.verify && this.db.verify == true) {
            //     _fields += " -_uuid -_appid"
            // }
            this.options["fields"] = _fields;
        }
        return this;
    }

    /**
     * 数据排序
     * @param  {[type]} order [description]
     * @return {[type]}       [description]
     */
    order(order = "_id ASC") {
        if (!is_string(order) || empty(order)) {
            this.options['order'] = ""
            return this;
        }
        let _order = order.split(' ');
        let _reverse = {};
        if (_order[1].toUpperCase() == "DESC") {
            _reverse[_order[0]] = -1;
        } else {
            _reverse[_order[0]] = 1;
        }
        this.options['order'] = _reverse;
        return this;
    }

    /**
     * 设置数据极限
     * @param  {String} limit [description]
     * @return {[type]}       [description]
     */
    limit(limit = "") {
        if (!empty(limit)) {
            let _limit = limit.split(',');
            if (_limit.length == 2) {
                this.options['skip'] = _limit[0] ? Number(_limit[0]) : 0;
                this.options['limit'] = _limit[1] ? Number(_limit[1]) : 0;
            } else {
                this.options['skip'] = 0;
                this.options['limit'] = Number(limit);
            }
        }
        return this;
    }


    /**
     * 设置数据极限
     * @param  {String} page [description]
     * @return {[type]}       [description]
     */
    page(page = "", size = "") {
        if (!empty(page) && !empty(size)) {
            this.options['skip'] = (Number(page) - 1) * Number(size);
            this.options['limit'] = Number(size);
        }
        return this;
    }

    /**
     * 指定查询条件
     * @param  {[type]} field     查询字段
     * @param  {[type]} op        查询表达式
     * @param  {[type]} condition 查询条件
     * @return {[type]}           [description]
     */
    where(field, op = null, condition = null) {
        //判断字段为数组
        if (Object.prototype.toString.call(field) === "[object Array]" && !empty(field) && op == null) {
            let condition = {};
            //1.单个元素分割拆件
            for (var key in field) {
                let item = field[key];
                let single = explode(" ", item);
                if (count(single) == 2) {
                    let match = this.expWhere(key, single[0], single[1]);
                    condition[key] = match[key]
                } else {
                    condition[key] = item;
                }
            }
            field = condition;
        }
        //处理
        if (Object.prototype.toString.call(field) === "[object Array]" && !empty(field) && op == "or") {
            let maybe = [];
            for (var key in field) {
                let item = field[key];
                maybe.push({ key: field[key] })
            }
            field = { $or: maybe }
        } else if (is_string(field) && op && condition == null) {
            field = this.expWhere(field, "eq", op);
        } else if (is_string(field) && op && condition) {
            field = this.expWhere(field, op, condition);
        }
        this.options['where'] = field;
        //1.字符分割拆解
        return this;
    }


    /**
     * 设置ID
     * @param {[type]} obj  [description]
     * @param {[type]} name [description]
     */
    setObjectId(obj, name = "_id") {
        for (var key in obj) {
            if (obj.hasOwnProperty(key)) {
                if ("object" == typeof(obj[key])) {
                    this.setObjectId(obj[key], name);
                } else if (key == name) {
                    obj[key] = ObjectId(obj[key])
                }
            }
        }
        return obj;
    }

    /**
     * where处理
     * @param  {[type]} field     [description]
     * @param  {[type]} op        [description]
     * @param  {[type]} condition [description]
     * @return {[type]}           [description]
     */
    expWhere(field, op = null, condition = null) {
        //返回类型
        let type = this.exp[op];
        let match = {};
        switch (type) {
            case 'gt':
                match[field] = { $gt: condition };
                break;
            case 'lt':
                match[field] = { $lt: lt };
                break;
            case 'gte':
                match[field] = { $gte: gte };
                break;
            case 'lte':
                match[field] = { $lte: lte };
                break;
            case 'ne':
                match[field] = { $ne: ne };
                break;
            default:
                match[field] = condition;
        }
        return match;
    }

    /**
     * 视图操作
     * @param  {[type]} name  [description]
     * @param  {[type]} from  [description]
     * @param  {[type]} field [description]
     * @param  {[type]} as    [description]
     * @return {[type]}       [description]
     */
    view(name, from, field, as = "") {
        this.options['name'] = name;
        let fields = explode("=", field)
        this.options['lookup'] = {
            from: from,
            localField: fields[0],
            foreignField: fields[1],
            as: as ? as : from
        }
        return this;

    }

    /**
     * 写入数据
     * @param  {Object} data [description]
     * @return {[type]}      [description]
     */
    async insert(data = {}) {
        try {
            if (empty(data)) {
                throw new Error('写入数据不能为空');
            }
            console.log(":::::::", 1)
            let connect = await this.connection();
            let collect = connect.db(this.database).collection(this.options['name']);
            //处理返回值
            let result;
            if (!Array.isArray(data)) {
                result = await collect.insertOne(data);
            } else {
                result = await collect.insertMany(data);
            }
            connect.close();
            return result.insertedId;
        } catch (error) {
            throw error;
        }
    }

    /**
     * 删除数据
     * @return {[type]} [description]
     */
    async delete() {
        try {
            if (!this.options['where']) {
                throw new Error('WHERE条件不能为空');
            }
            let connect = await this.connection();
            //处理对象ID
            let where = this.setObjectId(this.options['where']);
            let collect = connect.db(this.database).collection(this.options['name']);
            let reverse = await collect.deleteMany(where);
            if (reverse.deletedCount === 0) {
                return 0;
            } else {
                return true;
            }
        } catch (error) {
            throw error;
        }
    }

    /**
     * 更新数据
     * @return {[type]} [description]
     */
    async update(data) {
        try {
            if (!this.options['where']) {
                throw new Error('WHERE条件不能为空');
            }
            let connect = await this.connection();
            //处理对象ID
            let where = this.setObjectId(this.options['where']);
            //执行操作方法
            let collect = connect.db(this.database).collection(this.options['name']);
            let reverse = await collect.updateMany(where, { $set: data })
            connect.close();
            return true;
        } catch (error) {
            throw error;
        }
    }

    /**
     * 数据查询
     * @return {[type]} [description]
     */
    async select(fields = "") {
        try {
            let options = extend({
                where: {},
                order: { '_id ASC': -1 },
                skip: 0,
                limit: 20
            }, this.options)
            // console.log("options", options)
            //设置字段
            this.fields(fields);
            let connect = await this.connection();
            let where = this.setObjectId(options['where']);
            let collect = connect.db(this.database).collection(this.options['name']);
            //判断如果为视图模式
            if (this.options['lookup']) {
                let aggregate = [{
                    $lookup: this.options['lookup']
                }];
                if (!empty(options['where'])) {
                    aggregate.push({ $match: where });
                }
                let reverse = await collect.aggregate(aggregate)
                let data = await reverse.toArray();
                connect.close();
                return data;
            } else {
                let reverse = await collect.find(where, options['fields'])
                    .sort(options['order'])
                    .skip(options['skip'])
                    .limit(options['limit']);
                let data = await reverse.toArray();
                connect.close();
                return data;
            }
        } catch (error) {
            throw error;
        }

    }


    /**
     * 数据查询
     * @return {[type]} [description]
     */
    async first(fields = "") {
        try {
            let options = extend({
                where: {},
                skip: 0
            }, this.options)
            //console.log("options", options)
            //设置字段
            this.fields(fields);
            let connect = await this.connection();
            let collect = connect.db(this.database).collection(this.options['name']);
            //解析Where
            let where = this.setObjectId(options['where']);
            //判断如果为视图模式
            if (this.options['lookup']) {
                let aggregate = [{
                    $lookup: this.options['lookup']
                }];
                if (!empty(options['where'])) {
                    aggregate.push({ $match: where });
                }
                let reverse = await collect.aggregate(aggregate)
                let data = await reverse.toArray();
                connect.close();
                return data;
            } else {
                let reverse = await collect.findOne(where, options['fields']);
                connect.close();
                return reverse;
            }
        } catch (error) {
            throw error;
        }

    }
}

class sql {
    /**
     * 数据库连接实例
     * @type {Array}
     */
    static instance = [];

    /**
     * 数据库初始化，并取得数据库类实例
     * @access public
     * @param  mixed       config 连接配置
     * @param  bool|string name   连接标识 true 强制重新连接
     * @return Connection
     * @throws Exception
     */
    static connect(config = [], name = false) {
        if (false === name) {
            name = md5(to_json(config));
        }
        if (true === name || !isset(this.instance[name])) {
            let options = config;
            this.instance[name] = new mongodb(options);
        }
        return this.instance[name];
    }
}
module.exports = sql;